<?php

namespace GordenSong\Laravel\Support;

use Illuminate\Container\Container;
use Illuminate\Contracts\Container\BindingResolutionException;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;

class AutowireHelper
{
	/**
	 * @throws ReflectionException
	 * @throws BindingResolutionException
	 */
	public static function make(string $class, bool $recursive = false)
	{
		$instance = Container::getInstance()->make($class);

		$reflectionClass = new ReflectionClass($instance);
		$classAttributes = $reflectionClass->getAttributes(Autowire::class);
		if (count($classAttributes) > 0) {
			self::tryAutowireProperties($instance, $reflectionClass, $recursive);
		}

		return $instance;
	}

	/**
	 * @throws ReflectionException
	 * @throws BindingResolutionException
	 */
	public static function autowireProperties($object, bool $recursive = false)
	{
		self::tryAutowireProperties($object, new ReflectionClass($object), $recursive);
	}

	/**
	 * @param object $object
	 * @param ReflectionClass|null $reflectionClass
	 * @param bool $recursive
	 * @throws BindingResolutionException
	 * @throws ReflectionException
	 */
	protected static function tryAutowireProperties(object $object, ?ReflectionClass $reflectionClass = null, bool $recursive = false): void
	{
		$reflectionClass = $reflectionClass ?? new ReflectionClass($object);

		$properties = $reflectionClass->getProperties(ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PUBLIC);
		foreach ($properties as $property) {
			$attributes = $property->getAttributes(Autowire::class);
			if (count($attributes) > 0) {
				$property->setAccessible(true);
				if (is_null($property->getValue($object))) {
					$class = $property->getType()->getName();
					if ($recursive) {
						$value = AutowireHelper::make($class, $recursive);
					} else {
						$value = Container::getInstance()->make($class);
					}
					$property->setValue($object, $value);
				}
			}
		}
	}
}